package io.wongxd.solution.util.tools

import android.util.Log
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.roundToInt


private val sdf_yyyyMMddHHmmss = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT)
private val sdf_yyyyMMddHHmm = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ROOT)
private val sdf_yyyyMMdd = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
private val sdf_yyyyM = SimpleDateFormat("yyyy年M月", Locale.ROOT)
private val sdf_yyyyMd = SimpleDateFormat("yyyy年M月d日", Locale.ROOT)
private val sdf_M = SimpleDateFormat("M月", Locale.ROOT)
private val sdf_Md = SimpleDateFormat("M月d日", Locale.ROOT)
private val sdf_HHmm = SimpleDateFormat("HH:mm", Locale.ROOT)

val nowMillis: Tstamp
    get() = System.currentTimeMillis()
const val quarterMillis: Tstamp = 15 * 60 * 1000L
const val hourMillis: Tstamp = 60 * 60 * 1000L
const val dayMillis: Tstamp = 24 * hourMillis

/**
 * 时间戳
 */
typealias Tstamp = Long

val Tstamp.calendar: Calendar
    get() = Calendar.getInstance().apply { timeInMillis = this@calendar }

val Tstamp.years: Int
    get() = run {
        Calendar.getInstance().apply {
            time = Date(this@run)
        }.get(Calendar.YEAR)
    }

val Tstamp.monthOfYear: Int
    get() = run {
        Calendar.getInstance().apply {
            time = Date(this@run)
        }.get(Calendar.MONTH) + 1
    }

val Tstamp.dayOfMonth: Int
    get() = calendar.get(Calendar.DAY_OF_MONTH)

val Tstamp.dayOfYear: Int
    get() = calendar.get(Calendar.DAY_OF_YEAR)

val Tstamp.dayOfWeek: Int
    get() = calendar.get(Calendar.DAY_OF_WEEK)

val Tstamp.dayOfWeekText: String
    get() = run {
        when (calendar.get(Calendar.DAY_OF_WEEK)) {
            Calendar.SUNDAY -> "周日"
            Calendar.MONDAY -> "周一"
            Calendar.TUESDAY -> "周二"
            Calendar.WEDNESDAY -> "周三"
            Calendar.THURSDAY -> "周四"
            Calendar.FRIDAY -> "周五"
            else -> "周六"
        }
    }

val Tstamp.dayOfWeekTextSimple: String
    get() = run {
        when (calendar.get(Calendar.DAY_OF_WEEK)) {
            Calendar.SUNDAY -> "日"
            Calendar.MONDAY -> "一"
            Calendar.TUESDAY -> "二"
            Calendar.WEDNESDAY -> "三"
            Calendar.THURSDAY -> "四"
            Calendar.FRIDAY -> "五"
            else -> "六"
        }
    }

val Tstamp.hourOfDay: Int
    get() = run {
        Calendar.getInstance().apply {
            time = Date(this@run)
        }.get(Calendar.HOUR_OF_DAY)
    }

val Tstamp.minute: Int
    get() = run {
        Calendar.getInstance().apply {
            time = Date(this@run)
        }.get(Calendar.MINUTE)
    }

val Tstamp.second: Int
    get() = run {
        Calendar.getInstance().apply {
            time = Date(this@run)
        }.get(Calendar.SECOND)
    }

val Tstamp.yyyyMMddHHmmss: String
    get() = sdf_yyyyMMddHHmmss.format(this)

val Tstamp.yyyyMMddHHmm: String
    get() = sdf_yyyyMMddHHmm.format(this)

val Tstamp.yyyyMMdd: String
    get() = sdf_yyyyMMdd.format(this)

val Tstamp.yyyyMd: String
    get() = sdf_yyyyMd.format(this)

val Tstamp.yyyyM: String
    get() = sdf_yyyyM.format(this)

val Tstamp.Md: String
    get() = sdf_Md.format(this)

val Tstamp.M: String
    get() = sdf_M.format(this)

val Tstamp.HHmm: String
    get() = sdf_HHmm.format(this)

val Calendar.yyyyMMddHHmmss: String
    get() = timeInMillis.yyyyMMddHHmmss

val Calendar.yyyyMMddHHmm: String
    get() = timeInMillis.yyyyMMddHHmm

val Calendar.yyyyMd: String
    get() = timeInMillis.yyyyMd

val Calendar.yyyyM: String
    get() = timeInMillis.yyyyM

val Calendar.M: String
    get() = timeInMillis.M

val Calendar.Md: String
    get() = timeInMillis.Md

val Calendar.HHmm: String
    get() = timeInMillis.HHmm

val Calendar.firstDayOfMonthTime: Long
    get() = beginOfDay(timeInMillis).apply {
        set(Calendar.DAY_OF_MONTH, getActualMinimum(Calendar.DAY_OF_MONTH))
    }.timeInMillis

val Calendar.lastDayOfMonthTime: Long
    get() = beginOfDay(timeInMillis).apply {
        set(Calendar.DAY_OF_MONTH, getActualMaximum(Calendar.DAY_OF_MONTH))
    }.timeInMillis

val Calendar.firstDayOfWeekTime: Long
    get() = beginOfDay(timeInMillis).apply {
        set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY)
    }.timeInMillis

val Calendar.lastDayOfWeekTime: Long
    get() = beginOfDay(timeInMillis).apply {
        set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY)
    }.timeInMillis

fun beginOfDay(timestamp: Long = nowMillis): Calendar = Calendar.getInstance().apply {
    time = Date(timestamp)
    set(Calendar.HOUR_OF_DAY, 0)
    set(Calendar.MINUTE, 0)
    set(Calendar.SECOND, 0)
    set(Calendar.MILLISECOND, 0)
}

fun Tstamp.adjustTimestamp(period: Long, roundTo: Boolean): Long {
    val zeroOfDay = beginOfDay(this).timeInMillis
    return if (!roundTo) {
        this - (this - zeroOfDay) % period
    } else {
        (zeroOfDay + (1f * (this - zeroOfDay) / period).roundToInt() * period)
    }
}

fun Tstamp.adjustDuration(period: Long, roundTo: Boolean): Long {
    return if (roundTo) {
        (1f * this / period).roundToInt() * period
    } else {
        (1f * this / period).toInt() * period
    }
}

fun Tstamp.parseMonthIndex(scheduleBeginTime: Tstamp = 0L): Int {
    if (this == -1L) return nowMillis.parseMonthIndex(scheduleBeginTime)
    return run {
        val start = beginOfDay(scheduleBeginTime)
        val end = beginOfDay(this)
        val result =
            (end.get(Calendar.YEAR) - start.get(Calendar.YEAR)) * 12 + (end.get(Calendar.MONTH) - start.get(
                Calendar.MONTH
            ))
        result.apply { Log.i("MonthIndex", "month index = $result") }
    }
}

fun Tstamp.parseWeekIndex(scheduleBeginTime: Tstamp = 0L): Int {
    if (this == -1L) return nowMillis.parseWeekIndex(scheduleBeginTime)
    return run {
        val startWeekDay = beginOfDay(scheduleBeginTime).apply {
            timeInMillis -= (get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY) * dayMillis
        }.timeInMillis
        val result = ((this - startWeekDay) / (7 * dayMillis)).toInt()
        result.apply { Log.i("WeekIndex", "week index = $result") }
    }
}


/**
 * 此时间戳距当前多少天
 */
val Tstamp.deltaDays: Long
    get() = (beginOfDay(this).timeInMillis - beginOfDay().timeInMillis) / dayMillis

/**
 * 此时间戳距当前多少月
 */
val Tstamp.deltaMonths: Int
    get() = (years - nowMillis.years) * 12 + monthOfYear - nowMillis.monthOfYear

/**
 * 时间戳差值换算为时分秒
 *
 * @return
 */
fun Tstamp.second2HourMinuteSecond(needHour: Boolean = true): String = (this / 1000).toInt().secondChangeHourMinuteSecond()

/**
 * 时间戳差值换算为时分秒
 *
 * @return
 */
fun Tstamp.second2HMS(): Triple<String, String, String> = (this / 1000).toInt().secondChangeHMS()

/**
 * int 秒换算为时分秒
 */
private fun Int.secondChangeHourMinuteSecond(needHour: Boolean = true): String {
    var h = 0
    var m = 0
    var s = 0
    val temp = this % 3600
    if (this > 3600) {
        h = this / 3600
        if (temp != 0) {
            if (temp > 60) {
                m = temp / 60
                if (temp % 60 != 0) {
                    s = temp % 60
                }
            } else {
                s = temp
            }
        }
    } else { // 小于1小时
        // 当前时间除以60 获取当前是多少分钟
        m = this / 60
        if (this % 60 != 0) {
            s = this % 60
        }
    }
    val hStrings = if (h < 10) {
        "0${h}"
    } else {
        "$h"
    }
    val mStrings = if (m < 10) {
        "0${m}"
    } else {
        "$m"
    }
    val sStrings = if (s < 10) {
        "0${s}"
    } else {
        "$s"
    }
    return if (needHour)
        "${hStrings}:${mStrings}:${sStrings}"
    else
        "${mStrings}:${sStrings}"
}

private fun Int.secondChangeHMS(): Triple<String, String, String> {
    var h = 0
    var m = 0
    var s = 0
    val temp = this % 3600
    if (this > 3600) {
        h = this / 3600
        if (temp != 0) {
            if (temp > 60) {
                m = temp / 60
                if (temp % 60 != 0) {
                    s = temp % 60
                }
            } else {
                s = temp
            }
        }
    } else { // 小于1小时
        // 当前时间除以60 获取当前是多少分钟
        m = this / 60
        if (this % 60 != 0) {
            s = this % 60
        }
    }
    val hStrings = if (h < 10) {
        "0${h}"
    } else {
        "$h"
    }
    val mStrings = if (m < 10) {
        "0${m}"
    } else {
        "$m"
    }
    val sStrings = if (s < 10) {
        "0${s}"
    } else {
        "$s"
    }

    return Triple(hStrings, mStrings, sStrings)
}


/**
 * 计算两个日期所差的天数
 * 返回为 int = 0 则同一天
 *          < 0 为前一天
 */
infix fun Tstamp.differDay(milliseconds2: Long): Int {
    val dar1 = Calendar.getInstance()
    dar1.timeInMillis = this
    val dar2 = Calendar.getInstance()
    dar2.timeInMillis = milliseconds2
    // 先判断是否同年
    val y1 = dar1.get(Calendar.YEAR)
    val y2 = dar2.get(Calendar.YEAR)
    val d1 = dar1.get(Calendar.DAY_OF_YEAR)
    val d2 = dar2.get(Calendar.DAY_OF_YEAR)
    return if (y1 - y2 > 0) {
        d1 - d2 + dar2.getActualMaximum(Calendar.DAY_OF_YEAR)
    } else if (y1 - y2 < 0) {
        d1 - d2 - dar1.getActualMaximum(Calendar.DAY_OF_YEAR)
    } else {
        d1 - d2
    }
}

/**
 * 计算两个日期所差的小时数
 */
infix fun Tstamp.differHours(date2: Long): Int {
    val dar1 = Calendar.getInstance()
    dar1.timeInMillis = this
    val dar2 = Calendar.getInstance()
    dar2.timeInMillis = date2
    val h1: Int = dar1.get(Calendar.HOUR_OF_DAY)
    val h2: Int = dar2.get(Calendar.HOUR_OF_DAY)
    val day = this differDay date2
    return h1 - h2 + day * 24
}

/**
 * 计算两个日期所差的分钟数
 */
infix fun Tstamp.differMinutes(date2: Long): Int {
    val dar1 = Calendar.getInstance()
    dar1.timeInMillis = this
    val dar2 = Calendar.getInstance()
    dar2.timeInMillis = date2
    val m1: Int = dar1.get(Calendar.MINUTE)
    val m2: Int = dar2.get(Calendar.MINUTE)
    val h = this differHours date2
    return m1 - m2 + h * 60
}
